﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Tesseract.DataAccessBase.Attributes;

namespace Tesseract.DataAccessBase
{
    internal class StoredProcedure
    {
        internal string spName
        {
            get;
            set;
        }

        internal string Schema
        {
            get;
            set;
        }

        internal Dictionary<string, object> ParameterValueDictionary
        {
            get;
            set;
        }

        internal StoredProcedure(object entity, MethodBase method, Command command)
        {
            Type methodClassType = method.ReflectedType;
            ICollection<object> classAttribs = methodClassType.GetCustomAttributes(true);
            IgnoreEntityProperties ignoreColumns = (IgnoreEntityProperties)Attribute.GetCustomAttribute(method, typeof(IgnoreEntityProperties));

            GetDbSchemaName(methodClassType, classAttribs);

            GetStoredProcedureName(method);

            ParameterValueDictionary = new Dictionary<string, object>();

            int counter = 0;

            PropertyInfo[] p = entity.GetType().GetProperties();

            foreach (PropertyInfo info in p)
            {
                ParameterName pName = (ParameterName)Attribute.GetCustomAttribute(info, typeof(ParameterName));

                string parameterName = pName != null ? pName.Name : info.Name;

                bool ignore = false;

                switch (command)
                {
                    case Command.Insert:
                        //Make sure this is not one of the ignored properties or an Identity property.
                        ignore = (ignoreColumns != null && ignoreColumns.IgnoreNames.Contains(info.Name)) || Attribute.GetCustomAttribute(info, typeof(InsertIgnore)) != null;
                        break;
                    case Command.Update:
                        //Make sure this is not one of the ignored properties or an Identity property.
                        ignore = ignoreColumns != null && ignoreColumns.IgnoreNames.Contains(info.Name) || Attribute.GetCustomAttribute(info, typeof(UpdateIgnore)) != null;
                        break;
                    case Command.Delete:
                    case Command.Select:
                        break;
                    default:
                        throw new ArgumentOutOfRangeException("command");
                }

                if (!ignore)
                {
                    ParameterValueDictionary.Add(parameterName, p[counter].GetValue(entity, null));
                }

                counter++;
            }
        }

        internal StoredProcedure(MethodBase method, IList<object> p)
        {
            Type methodClassType = method.ReflectedType;
            ICollection<object> classAttribs = methodClassType.GetCustomAttributes(true);

            GetDbSchemaName(methodClassType, classAttribs);

            GetStoredProcedureName(method);

            ParameterValueDictionary = new Dictionary<string, object>();

            int counter = 0;

            if (p.Count <= 0) return;

            try
            {
                foreach (ParameterInfo info in method.GetParameters())
                {
                    ParameterValueDictionary.Add(info.Name, p[counter]);

                    counter++;
                }

            }
            catch (Exception ex)
            {
                throw new Exception("Method parameter count do not match parameters passed to Exec", ex);
            }
        }

        internal void GetDbSchemaName(Type methodClassType, ICollection<object> classAttribs)
        {
            SchemaName schemaName = null;

            if (classAttribs != null && classAttribs.Count > 0)
            {
                schemaName = (SchemaName)(from a in classAttribs
                                          where a.GetType().Equals(typeof(SchemaName))
                                          select a).SingleOrDefault();
            }

            Schema = schemaName != null && schemaName.Name != null ? schemaName.Name : methodClassType.Name;
        }

        internal void GetStoredProcedureName(MethodBase method)
        {
            ICollection<object> methodAttribs = method.GetCustomAttributes(false);

            StoredProcedureName sprocName = null;

            if (methodAttribs != null && methodAttribs.Count > 0)
            {
                sprocName = (StoredProcedureName)(from a in methodAttribs
                                                  where
                                                      a.GetType().Equals(
                                                          typeof(StoredProcedureName))
                                                  select a).SingleOrDefault();
            }

            spName = sprocName != null && sprocName.Name != null ? sprocName.Name : method.Name;
        }
    }
}
